﻿using System.Collections.Generic;
using System;
using System.Reflection;
using IslandUnit.Exceptions;

namespace IslandUnit.Providers
{
    public class ProviderRepository
    {
        private static ProviderRepository _providerRepository;
        private bool _discovered;

        public MockProviderBase MockProvider { get; private set; }
        public BuilderProviderBase BuilderProvider { get; private set; }
        public ContainerProviderBase ContainerProvider { get; private set; }
        public Assembly ContainerAssembly { get; private set; }
        public Type ContainerType { get; private set; }

        private void Discover()
        {
            this.MockProvider = new MoqProvider();
            this.BuilderProvider = new NBuilderProvider();
            
            this.DiscoverContainerFramework();
            this.ContainerType = this.ContainerAssembly.GetType("Ninject.StandardKernel");

            this._discovered = true;
        }

        private void DiscoverContainerFramework()
        {
            IList<ProviderMapping> mappings = new List<ProviderMapping>();
            mappings.Add(new ProviderMapping { AssemblyName = "Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7", ProviderType = typeof(NinjectProvider) });
            mappings.Add(new ProviderMapping { AssemblyName = "Ninject, Version=2.1.0.76, Culture=neutral, PublicKeyToken=c7192dc5380945e7", ProviderType = typeof(NinjectProvider) });

            foreach (ProviderMapping mapping in mappings)
            {
                Assembly assembly = TryLoadAssembly(mapping.AssemblyName);

                if (assembly != null)
                {
                    this.ContainerAssembly = assembly;
                    this.ContainerProvider = (ContainerProviderBase)Activator.CreateInstance(mapping.ProviderType);
                    return;
                }
            }

            throw new DiscoverException("Could not find any IoC containers");
        }

        private static Assembly TryLoadAssembly(string assemblyName)
        {
            try
            {
                Assembly assembly = Assembly.Load(assemblyName);
                
                return assembly;
            }
            catch (System.IO.FileLoadException)
            {
                return null;
            }
        }

        public static ProviderRepository Instance()
        {
            if (_providerRepository == null)
            {
                _providerRepository = new ProviderRepository();
            }

            if (!_providerRepository._discovered)
            {
                // Here we will "discover" which frameworks (Mock, Builder
                // and IoC container) to use
                _providerRepository.Discover();
            }

            return _providerRepository;
        }
    }
}
